Like any kind of apps, JavaScript apps also have to be written well.
Otherwise, we run into all kinds of issues later on.
In this article, we’ll look at some best practices we should follow when writing Node apps.
Use Semantic Versioning
We should use semantic version to version our app.
It’s conventional so that many people will understand it.
The version should have the major version, minor version, and bug fix version and separated by dots.
So the format is major.minor.bugfix
Secure Our Applications
We should secure user and customer data so that we can protect our app against any attacks.
Things that we should be aware of include security HTTP headers, brute force attacks, and more.
We should have some headers in our HTTP response.
They include Strict-Transport-Security which enforces HTTPS connections to the server.
X-Frame-Options provides clickjacking protection.
X-XSS-Protection enables cross-site scripting filter built into most recent web browsers.
X-Content-Type-Options prevents browsers from MIME-sniff a response from the declared content type.
Content-Security-Policy prevents a wide range of attacks like cross-site scripting and other cross-site injections.
We can enable all of them with the Helmet module:
const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet());
There’s also the Koa version, which is the koa-helmet module.
Also, we can use it in the Nginx seer to add the headers.
In nginx.conf
, we can add:
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "default-src 'self'";
Sensitive Data on the Client Side
We should never expose API secrets and credentials in our source code since it’ll be readable by anyone.
We can check them in code reviews.
Brute Force Protection
Brute force protection should be added to our app so that we can prevent these attacks.
To do this, we add a rate-limiting library to limit the requests that can be made.
We can use the ratelimiter package to limit the number of times a function is called:
const limit = new Limiter({ id, db });
limit.get((err, limit) => {
});
We can write Express middleware with it.
For example, we can write:
const ratelimit = require('koa-ratelimit');
const redis = require('redis');
constkoa = require('koa');
constapp = koa();
const emailBasedRatelimit = ratelimit({
db: redis.createClient(),
duration: 100000,
max: 10,
id(context) {
return context.body.email;
}
});
const ipBasedRatelimit = ratelimit({
db: redis.createClient(),
duration: 100000,
max: 10,
id(context) {
return context.ip;
}
});
app.post('/login', ipBasedRatelimit, emailBasedRatelimit, handleLogin);
We check the ID and email with our rate limit middleware with the id
method.
duration
sets the duration.
db
is the Redi connection instance.
max
is the max number of requests.
Session Management
To secure our cookies, we set a few flags.
One of them is the secure
flag. This tells the browser to only send the cookie if the request is being sent over HTTPS.
HttpOnly
is used to help prevent attacks like cross-site scripting since it disallows cookies to be accessed with JavaScript.
The scope of the domain should also be changed.
The domain
compares against the domain of the server to which the URL is being requested.
If the domain matches, then the path has to match.
Once the path
is checked then the cookie will be sent with the request.
expires
is the attribute used to set persistent cookies. They expire after the expiry date has passed.
Conclusion
We should use semantic version and take some steps to secure our app.